﻿using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;
using System.Collections.Generic;

namespace Projekt_naczepa
{
    public class Command
    {
        public int cmd;
        public int size;
        public byte[] data;
    }
    public class UDPConnection
    {
        private UdpClient client_in = null;
        private UdpClient client_out = null;

        public const int HELLO_RPI = 1;
        public const int LUA = 100;
        public const int INTERNAL = 110;
        public const int IMAGE_START = 200;
        public const int IMAGE_STOP = 201;
        public const int IMAGE_DATA = 202;
        public const int REC_OK = 300;

        private bool shall_receive_ = false;
        private Mutex mutex_;
        private List<Command> receivedCommands_;
        private Thread thread_;
        private bool IS_REC_OK = false;
        public byte[] image_data = null;
        public float progress = 0.0f;
        public UDPConnection()
        {
            mutex_ = new Mutex();
            receivedCommands_ = new List<Command>();
            thread_ = new Thread(new ThreadStart(this.receiverThread));
        }
        public bool connect(string addr, int port)
        {
            if (client_in != null || client_out != null)
            {
                return true;
            }
            client_in = new UdpClient(addr, port);
            client_out = client_in;//new UdpClient(addr,port);

            thread_.Start();
            return false;
        }
        public void disconnect()
        {
            shall_receive_ = false;
            thread_.Join(500);
            thread_.Abort();
            if (client_in != null)
            {
                client_in.Close();
                client_in = null;
            }
            if (client_out != null)
            {
                client_out.Close();
                client_out = null;
            }
        }
        public Command getCommand()
        {
            mutex_.WaitOne();
            if (receivedCommands_.Count == 0)
            {
                mutex_.ReleaseMutex();
                return null;
            }
            Command cmd = receivedCommands_[0];
            receivedCommands_.RemoveAt(0);
            mutex_.ReleaseMutex();
            return cmd;
        }
        public byte[] createDatagram(int command, string str)
        {

            byte[] data_gram = new byte[str.Length + 8];//4 - cmd + 4 - size
            byte[] cmd_bytes = BitConverter.GetBytes(command);
            byte[] size_bytes = BitConverter.GetBytes(str.Length);
            byte[] ascii_str = Encoding.ASCII.GetBytes(str);
            cmd_bytes.CopyTo(data_gram, 0);

            Buffer.BlockCopy(cmd_bytes, 0, data_gram, 0, 4);
            Buffer.BlockCopy(size_bytes, 0, data_gram, 4, 4);
            Buffer.BlockCopy(ascii_str, 0, data_gram, 8, str.Length);

            return data_gram;
        }
        public bool sendHello()
        {
            int cnt = 10;

            while (true)
            {
                byte[] msg = createDatagram(HELLO_RPI, "");
                int send_size = client_out.Send(msg, msg.Length);
                if (send_size != msg.Length) return true;
                for (uint i = 0; i < 100; i++)
                {
                    if (isRecOk() == true)
                    {
                        System.Console.Write("Hello OK\n");
                        return false;
                    }
                    System.Threading.Thread.Sleep(1);
                }
                System.Console.Write("Rec_Timeout!\n");
                cnt--;
                if (cnt <= 0)
                {
                    System.Console.Write("cnt<=0\n");
                    return true;
                }
            }
        }
        public void sendOk()
        {
            byte[] msg = createDatagram(UDPConnection.REC_OK, "");
            mutex_.WaitOne();
            client_out.Send(msg, msg.Length);
            mutex_.ReleaseMutex();
        }
        public bool isRecOk()
        {
            bool rc = IS_REC_OK;
            IS_REC_OK = false;
            return rc;
        }
        public bool sendCommand(int cmd, string str)
        {
            int cnt = 10;
            byte[] msg = createDatagram(cmd, str);
            while (true)
            {
                mutex_.WaitOne();
                int send_size = client_out.Send(msg, msg.Length);
                mutex_.ReleaseMutex();
                if (send_size != msg.Length) return true;
                for (uint i = 0; i < 50; i++)
                {
                    if (isRecOk() == true) return false;
                    System.Threading.Thread.Sleep(10);
                }
                System.Console.Write("Rec_Timeout!\n");
                cnt--;
                if (cnt <= 0)
                {
                    System.Console.Write("cnt<=0\n");
                    return true;
                }
            }
        }
        private void receiverThread()
        {
            shall_receive_ = true;
            IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
            while (shall_receive_)
            {
                byte[] rec = client_in.Receive(ref RemoteIpEndPoint);
                Command cmd = new Command();
                cmd.cmd = BitConverter.ToInt32(rec, 0);
                cmd.size = BitConverter.ToInt32(rec, 4);
                cmd.data = new byte[cmd.size + 1];
                cmd.data[cmd.size] = 0;
                Buffer.BlockCopy(rec, 8, cmd.data, 0, cmd.size);

                if (cmd.cmd == UDPConnection.REC_OK)
                { IS_REC_OK = true; }
                else
                {
                    if (!incomingImageDataHandling(cmd))
                    {
                        mutex_.WaitOne();
                        receivedCommands_.Add(cmd);
                        mutex_.ReleaseMutex();
                    }
                    sendOk();
                }
            }
        }
        private bool incomingImageDataHandling(Command cmd)
        {
            if (cmd.cmd > 210 || cmd.cmd < 200)
                return false;

            if (cmd.cmd == UDPConnection.IMAGE_START)
            {
                progress = 0.0f;
                System.Console.Write("IMAGE_START\n");
                if (cmd.size != 5)
                    return true;
                int data_size = BitConverter.ToInt32(cmd.data, 0);
                if (data_size > 1000 * 1000 * 3)
                    return true;
                image_data = new byte[data_size];
                return false;
            }
            if (cmd.cmd == UDPConnection.IMAGE_DATA)
            {
                int img_data_size = cmd.size - 4;
                progress += ((float)img_data_size) / ((float)image_data.Length);
                int img_data_offset = BitConverter.ToInt32(cmd.data, 0);
                Buffer.BlockCopy(cmd.data, 4, image_data, img_data_offset, img_data_size);
                return true;
            }
            if (cmd.cmd == UDPConnection.IMAGE_STOP)
            {
                //progress = 1.0f;
                System.Console.Write("IMAGE_STOP\n");
                return false;
            }
            return true;
        }
    }
    public class CommandTranslator
    {
        private string msg_string;
        private int is_png_comp;
        public void translate(Command cmd)
        {
            if (cmd.cmd == UDPConnection.LUA)
            {
                msg_string = Encoding.ASCII.GetString(cmd.data);
            }
            if (cmd.cmd == UDPConnection.IMAGE_START)
            {
                is_png_comp = (int)cmd.data[4];
            }
        }
        public string getString() { return msg_string; }
        public int isPNG() { return is_png_comp; }
    }
}

